// @Title business.go
// @Description cava smu服务主要业务逻辑
// @Author yezhibin 2021年5月1日01:22:46
// @Update

package business

import (
	"bytes"
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"strconv"
	"strings"
	"time"

	"go.uber.org/zap"
	"golang.org/x/text/encoding/simplifiedchinese"

	"Projects/CavaOpenAPI/internal/common"
	"Projects/CavaOpenAPI/internal/db"
	"Projects/CavaOpenAPI/internal/qfcfg"
	"Projects/CavaOpenAPI/internal/redis"
	util "Projects/CavaOpenAPI/internal/util"

	"github.com/go-basic/uuid"
	jsoniter "github.com/json-iterator/go"
	"github.com/kataras/iris/v12"
	"github.com/qf-tech/qftech-base/pkg/log"
	qflog "github.com/qf-tech/qftech-base/pkg/log"
	"nhooyr.io/websocket"
	"nhooyr.io/websocket/wsjson"
)

// Controller def
type Controller struct {
}

// Charset def
type Charset string

// const def
const (
	UTF8    = Charset("UTF-8")
	GB18030 = Charset("GB18030")
)

// Init def
func (control Controller) Init() {
	// 创建连接关闭通道
	closeCh = make(chan string, 1)

	// 初始化消息接收缓存通道，size=200，并启动接收消息协程
	recvMsgChan = make(chan RecvRemoteMsg, 200)
	go control.recvDataLoop(recvMsgChan, closeCh)

	// 初始化消息发送缓存通道，size=200，并启动发送消息协程
	sendMsgChan = make(chan SendRemoteMsg, 200)
	go control.sendDataLoop(sendMsgChan, closeCh)

	// 初始化任务请求消息通道，size=200
	taskReqChan = make(chan TaskReqMsg, 200)

	msgTypeMap = map[int]string{
		cavaClientRegisterReqMsg:    "cavaClientRegisterReqMsg",
		cavaClientRegisterRespMsg:   "cavaClientRegisterRespMsg",
		cavaTaskReqMsg:              "cavaTaskReqMsg",
		cavaTaskRespMsg:             "cavaTaskRespMsg",
		cavaTaskReportNotifyReqMsg:  "cavaTaskReportNotifyReqMsg",
		cavaTaskReportNotifyRespMsg: "cavaTaskReportNotifyRespMsg",
		cavaRegisterReqMsg:          "cavaRegisterReqMsg",
		cavaRegisterRespMsg:         "cavaRegisterRespMsg",
		cavaTaskStatusReqMsg:        "cavaTaskStatusReqMsg",
		cavaTaskStatusRespMsg:       "cavaTaskStatusRespMsg",
		cavaKeepAliveReqMsg:         "cavaKeepAliveReqMsg",
		cavaKeepAliveRespMsg:        "cavaKeepAliveRespMsg",
	}

	AliveCount = 0
}

func (control Controller) genAppID() string {
	tmpSlice := strings.Split(uuid.New(), "-")
	return tmpSlice[len(tmpSlice)-1]
}

func (control Controller) genUUID() string {
	return strings.ReplaceAll(uuid.New(), "-", "")
}

// CheckTraceID def
func (control Controller) CheckTraceID(c iris.Context) {
	traceID := c.GetHeader(common.TraceID)
	if traceID == "" {
		tmpSlice := strings.Split(uuid.New(), "-")
		traceID = tmpSlice[len(tmpSlice)-1]

		c.Request().Header.Set(common.TraceID, traceID)
		log.Sugare.Infof("Request generate trace id: %s, uri: %s method: %s", traceID, c.Request().RequestURI, c.Method())
	}
	ctx, _ := log.GetLogger().AddCtx(c.Request().Context(), zap.Any("TraceID", traceID))
	c.ResetRequest(c.Request().WithContext(ctx))
	c.Header(common.TraceID, traceID)
}
func JSONError(c iris.Context, status int, errMsg string) {
	log := log.GetLogger().GetCtx(c.Request().Context()).Sugar()
	c.StatusCode(status)

	type Err struct {
		Message string `json:"message"`
		Detail  string `json:"detail,omitempty"`
		Code    int    `json:"code,omitempty"`
	}

	type Response struct {
		Error Err `json:"error"`
	}

	resp := Response{
		Error: Err{
			Message: errMsg,
		},
	}

	_, err := c.JSON(resp)
	if err != nil {
		log.Errorf("Failed to encode: %v", err)
		return
	}

	retMsg, _ := jsoniter.Marshal(resp)
	log.Infof("response data: %s", string(retMsg))
}

func JSONResp(c iris.Context, status int, v interface{}) {
	log := log.GetLogger().GetCtx(c.Request().Context()).Sugar()
	c.StatusCode(status)

	resp := map[string]interface{}{
		"data": v,
	}

	_, err := c.JSON(resp)
	if err != nil {
		log.Errorf("Failed to encode: %v", err)
		return
	}

	retMsg, _ := jsoniter.Marshal(resp)
	log.Infof("response data: %s", string(retMsg))
}

func (control Controller) genTraceID() string {
	uid := uuid.New()
	tmpSlice := strings.Split(uid, "-")
	slicelen := len(tmpSlice)
	traceID := tmpSlice[slicelen-1]
	return traceID
}

// transToStr def
func (control Controller) transToStr(v interface{}) string {
	mJSON, err := json.Marshal(v)
	if err != nil {
		qflog.Sugare.Errorf("err:%v", err)
		return ""
	}
	msg := string(mJSON)
	return msg
}

// sendMsgToRemote def
func (control Controller) sendMsgToRemote(clientMsg SendRemoteMsg, ch chan string) {
	msg := control.transToStr(clientMsg.msg)
	dataMap := control.jsonToMap(msg)

	// 获取消息体中的traceID，获取不到则使用发送结构体里的traceID字段
	var traceID string
	if tmpID, ok := dataMap["trace_id"]; ok {
		traceID = fmt.Sprintf("%v", tmpID)
	}
	if traceID == "" {
		traceID = clientMsg.traceID
	}

	conn := clientMsg.conn
	err := wsjson.Write(clientMsg.ctx, conn, clientMsg.msg)
	if err != nil {
		var closeErr websocket.CloseError
		if errors.As(err, &closeErr) {
			qflog.Sugare.Warnf("TraceID=%s|conn already closed, client addr=%s", traceID, clientMsg.clientAddr)
		}
		ch <- clientMsg.clientAddr
		qflog.Sugare.Errorf("TraceID=%s|client addr=%s, err:%v", traceID, clientMsg.clientAddr, err)
	}
	qflog.Sugare.Infof("TraceID=%s|send data to server, data=%s, client addr=%s", traceID, msg, clientMsg.clientAddr)
}

func (control Controller) pushSendMsg(msgSt RecvRemoteMsg, sendMsg interface{}) {
	var sendMsgSt SendRemoteMsg
	sendMsgSt.conn = msgSt.conn
	sendMsgSt.clientAddr = msgSt.clientAddr
	sendMsgSt.msg = sendMsg
	sendMsgSt.ctx = msgSt.ctx
	sendMsgSt.traceID = msgSt.traceID
	sendMsgChan <- sendMsgSt
}

// sendDataLoop def
func (control Controller) sendDataLoop(ch chan SendRemoteMsg, closeCh chan string) {
	for msgSt := range ch {
		control.sendMsgToRemote(msgSt, closeCh)
	}
}

// jsonToMap def
func (control Controller) jsonToMap(str string) map[string]interface{} {

	var tempMap map[string]interface{}
	err := json.Unmarshal([]byte(str), &tempMap)
	if err != nil {
		qflog.Sugare.Errorf("err:%v", err)
		return tempMap
	}

	return tempMap
}

// ConvertByte2String def
func ConvertByte2String(byte []byte, charset Charset) string {
	var str string
	switch charset {
	case GB18030:
		var decodeBytes, _ = simplifiedchinese.GB18030.NewDecoder().Bytes(byte)
		str = string(decodeBytes)
	case UTF8:
		fallthrough
	default:
		str = string(byte)
	}
	return str
}

// AliveCount export var
var AliveCount int

// doKeepAlive def
func (control Controller) doKeepAlive(ctx context.Context, msgSt RecvRemoteMsg) {
	exitFlag := false
	for {
		if exitFlag {
			qflog.Sugare.Warnf("TraceID=%s|conn closed, exit keep alive, client addr=%s", msgSt.traceID, msgSt.clientAddr)
			break
		}

		// 待发送的消息写入对应channel
		var reqMsgSt KeepAliveReqMsg
		reqMsgSt.MsgCmd = cavaKeepAliveReqMsg
		reqMsgSt.NetID = "cava_client"
		reqMsgSt.TraceID = control.genTraceID()

		// 超时机制
		timeout := make(chan bool, 1)
		go func() {
			time.Sleep(30 * time.Second)
			timeout <- true
		}()

		select {
		case <-ctx.Done():
			exitFlag = true
		case <-timeout:
			control.pushSendMsg(msgSt, reqMsgSt)
			AliveCount++
		}
	}
}

// doTaskReq def
func (control Controller) doTaskReq(ctx context.Context, msgSt RecvRemoteMsg, ch chan TaskReqMsg) {
	exitFlag := false
	for {
		if exitFlag {
			qflog.Sugare.Warnf("TraceID=%s|conn closed, exit task request, client addr=%s", msgSt.traceID, msgSt.clientAddr)
			break
		}

		select {
		case <-ctx.Done():
			exitFlag = true
		case taskReq := <-ch:
			control.pushSendMsg(msgSt, taskReq)
		}

	}
}

// registerToSmu def
func (control Controller) registerToSmu(ctx context.Context, conn *websocket.Conn) {
	traceID := control.genTraceID()

	// 构造1个假的接收消息结构体，方便向smu主动发送消息
	var msgSt RecvRemoteMsg
	msgSt.conn = conn
	msgSt.clientAddr = clientAddr
	msgSt.msg = nil
	msgSt.ctx = ctx
	msgSt.traceID = traceID

	ts := strconv.Itoa(int(time.Now().Unix()))
	random := control.genTraceID()
	server := qfcfg.ServerConfig.Server
	plainData := fmt.Sprintf("%s%s%s%s", server.Username, server.Password, random, ts)
	digest := util.CalculateHash(plainData)

	// 待发送的消息写入对应channel
	var reqMsgSt ClientRegisterReqMsg
	reqMsgSt.MsgCmd = cavaClientRegisterReqMsg
	reqMsgSt.Username = server.Username
	reqMsgSt.Nonce = digest
	reqMsgSt.Random = random
	reqMsgSt.Timestamp = ts
	reqMsgSt.NetID = "cava_client"
	reqMsgSt.TraceID = control.genTraceID()

	control.pushSendMsg(msgSt, reqMsgSt)
}

func (control Controller) loop(recvMsg RecvRemoteMsg, closeCh chan string) {
	msg := control.transToStr(recvMsg.msg)
	var registerRespMsgSt ClientRegisterRespMsg
	_ = json.Unmarshal([]byte(msg), &registerRespMsgSt)

	if registerRespMsgSt.Code != 200 {
		qflog.Sugare.Errorf("TarceID=%s|client register to smu failed, code:%d client addr:%s", recvMsg.traceID, registerRespMsgSt.Code, recvMsg.clientAddr)
		recvMsg.conn.Close(websocket.StatusNormalClosure, "register to smu failed")
		return
	}

	ctx, cancel := context.WithCancel(context.Background())
	go control.doKeepAlive(ctx, recvMsg)
	go control.doTaskReq(ctx, recvMsg, taskReqChan)

	// 如果获取到的 close 消息是当前链路，则关闭
	var flag bool
	for ch := range closeCh {
		flag = ch == recvMsg.clientAddr
		log.Sugare.Infof("TarceID=%s|ch: %s, client addr=%s", recvMsg.traceID, ch, clientAddr)
		if flag {
			break
		}
	}

	log.Sugare.Warnf("TraceID=%s|cancel do keep alive goroutine, client addr=%s", recvMsg.traceID, recvMsg.clientAddr)
	cancel()
}

// processData def
func (control Controller) processData(recvMsg RecvRemoteMsg, closeCh chan string) {
	msg := control.transToStr(recvMsg.msg)
	dataMap := control.jsonToMap(msg)

	// 获取消息体中的traceID，获取不到则临时生成1个
	var traceID string
	if tmpID, ok := dataMap["trace_id"]; ok {
		traceID = fmt.Sprintf("%v", tmpID)
	}
	if traceID == "" {
		traceID = control.genTraceID()
	}

	if cmd, ok1 := dataMap["msg_cmd"]; ok1 {
		// interface{} 间接转int
		cmdStr := fmt.Sprintf("%v", cmd)
		cmdVal, _ := strconv.Atoi(cmdStr)
		qflog.Sugare.Infof("TraceID=%s|processData, cmd=%d, type=%s, recv data from server, data=%s, client addr=%s", traceID,
			cmdVal, msgTypeMap[cmdVal], msg, recvMsg.clientAddr)

		switch cmdVal {
		case cavaClientRegisterRespMsg:
			go control.loop(recvMsg, closeCh)
		}
	}
}

// recvDataLoop def
func (control Controller) recvDataLoop(ch chan RecvRemoteMsg, closeCh chan string) {
	for msgSt := range ch {
		control.processData(msgSt, closeCh)
	}
}

var clientAddr string

// readData def
func (control Controller) readDataLoop(ctx context.Context, conn *websocket.Conn, ch chan string) {
	for {
		var v interface{}
		err := wsjson.Read(ctx, conn, &v)
		if err != nil {
			var closeErr websocket.CloseError
			if errors.As(err, &closeErr) {
				qflog.Sugare.Warnf("conn already closed, client addr=%s", clientAddr)
			}
			ch <- clientAddr
			qflog.Sugare.Errorf("err:%v", err)
			break
		}

		msg := control.transToStr(v)
		dataMap := control.jsonToMap(msg)
		// 获取消息体中的traceID，获取不到则临时生成1个
		var traceID string
		if tmpID, ok := dataMap["trace_id"]; ok {
			traceID = fmt.Sprintf("%v", tmpID)
		}
		if traceID == "" {
			traceID = control.genTraceID()
		}

		// 解析客户端地址，赋值给全局变量
		if clientAddr == "" {
			ok := false
			if _, ok = dataMap["clientAddr"]; ok {
				clientAddr = fmt.Sprintf("%v", dataMap["clientAddr"])
				qflog.Sugare.Infof("TraceID=%s|client addr:%s", traceID, clientAddr)
				// 向smu注册
				control.registerToSmu(ctx, conn)
			}
		}

		// 接收到的消息写入对应channel
		var msgSt RecvRemoteMsg
		msgSt.conn = conn
		msgSt.clientAddr = clientAddr
		msgSt.msg = v
		msgSt.ctx = ctx
		msgSt.traceID = traceID
		recvMsgChan <- msgSt
	}
}

// Start def
func (control Controller) Start() {
	traceID := control.genTraceID()
	clientAddr = ""

	ctx := context.Background()
	// ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
	// defer cancel()

	url := fmt.Sprintf("ws://%s:%d/ws", qfcfg.ServerConfig.Server.IP, qfcfg.ServerConfig.Server.Port)
	conn, _, err := websocket.Dial(ctx, url, nil)
	if err != nil {
		qflog.Sugare.Errorf("err:%v", err)
		return
	}

	defer conn.Close(websocket.StatusInternalError, "内部错误")
	qflog.Sugare.Infof("TarceID=%s|client connect succeed, server addr=%s:%d", traceID, qfcfg.ServerConfig.Server.IP, qfcfg.ServerConfig.Server.Port)

	// 开启读数据协程
	go control.readDataLoop(ctx, conn, closeCh)

	// 如果获取到的 close 消息是当前链路，则关闭
	var flag bool
	for ch := range closeCh {
		flag = ch == clientAddr
		if flag {
			break
		}
	}
	qflog.Sugare.Warnf("TarceID=%s|exit go routinue: %v, client addr=%s", traceID, flag, clientAddr)
}

func (control Controller) PreCheckToken(c iris.Context) {
	log := log.GetLogger().GetCtx(c.Request().Context()).Sugar()

	if !qfcfg.ServerConfig.Client.AuthEnable {
		log.Warn("auth disenable, no need to check accessToken")
		c.Next()
		return
	}

	accessToken := c.URLParam("accessToken")
	if accessToken == "" {
		log.Error("accessToken is empty")
		JSONError(c, http.StatusBadRequest, "accessToken is empty")
		return
	}

	appID, err := redis.GetDigestInfo(c, accessToken)
	if err != nil && err.Error() == "redigo: nil returned" {
		log.Errorf("get accessToken from redis err: %v", err)
		JSONError(c, http.StatusBadRequest, "accessToken not exist, maybe wrong or updated")
		return
	} else if err != nil {
		log.Errorf("get accessToken from redis err: %v", err)
		JSONError(c, http.StatusBadRequest, "get accessToken from redis err")
		return
	}

	getOpts := db.AppGetOptions{
		AppID:  appID,
		IsFlag: -1,
	}

	result, err := db.App.Get(c, getOpts)
	if err != nil {
		log.Errorf("app get err: %v ", err)
		JSONError(c, http.StatusBadRequest, "app get err")
		return
	}

	if len(result.AppsInfo) == 0 {
		log.Error("appID is wrong")
		JSONError(c, http.StatusBadRequest, "appID is wrong")
		return
	}

	if result.AppsInfo[0].IsFlag == 1 {
		log.Error("have no permission to get accessToken")
		JSONError(c, http.StatusBadRequest, "have no permission to get accessToken")
		return
	}

	// 更新 token 后可能没从 redis 及时删除 老的 token
	if result.AppsInfo[0].AccessToken != accessToken {
		log.Error("accessToken maybe expired or updated")
		JSONError(c, http.StatusBadRequest, "accessToken maybe expired or updated")
		return
	}
	c.Next()
}

func (control Controller) CheckToken(c iris.Context) {
	log := log.GetLogger().GetCtx(c.Request().Context()).Sugar()
	accessToken := c.URLParam("accessToken")

	ttl, err := redis.GetTTL(c, accessToken)
	if err != nil {
		log.Errorf("get ttl accessToken from redis err: %v", err)
		JSONError(c, http.StatusBadRequest, "get ttl accessToken from redis err")
		return
	}

	var resp = struct {
		TTL       int64  `json:"ttl"`
		UpdatedAt string `json:"updatedAt"`
	}{
		TTL:       ttl,
		UpdatedAt: time.Now().Format("2006-01-02T15:04:05.999Z07:00"),
	}
	JSONResp(c, http.StatusOK, resp)
}

// AddXWsse 请求头增加 X-WSSE 认证头
func (control Controller) AddXWsse(c iris.Context) {
	log := log.GetLogger().GetCtx(c.Request().Context()).Sugar()
	wsse := c.GetHeader(common.XWsse)
	if wsse != "" {
		c.Next()
		return
	}

	s := qfcfg.ServerConfig.Server
	wsse = util.GenWsse(s.Username, s.Password, control.genTraceID())
	c.Request().Header.Set(common.XWsse, wsse)
	log.Infof("request add header, name: %s, value: %s", common.XWsse, wsse)
	c.Next()
}

// POST /api/secret/refresh
// SecretFresh 更新 secret
func (control Controller) SecretFresh(c iris.Context) {
	log := log.GetLogger().GetCtx(c.Request().Context()).Sugar()
	server := qfcfg.ServerConfig.Server
	url := fmt.Sprintf("http://%s:%d/authorization", server.HttpIP, server.Httpport)

	headers := map[string]string{
		"Content-Type": "application/json;charset=UTF-8",
		common.TraceID: c.GetHeader(common.TraceID),
	}

	tmpData, err := ioutil.ReadAll(c.Request().Body)
	if err != nil {
		log.Errorf("read all from request body err: %v", err)
		JSONError(c, http.StatusBadRequest, "read all from request body err")
		return
	}

	var req map[string]string
	if err := jsoniter.Unmarshal(tmpData, &req); err != nil {
		log.Errorf("trans byte to map fail, err: %v", err)
		JSONError(c, http.StatusBadRequest, "trans byte to map error")
		return
	}
	log.Infof("req url: %s method: POST, req data: %s", url, string(tmpData))

	if req["appname"] == "" {
		log.Error("appname is empty")
		JSONError(c, http.StatusBadRequest, "appname is empty")
		return
	}

	v, err := util.HttpPostJson(url, &req, headers, 10*time.Second)
	if err != nil {
		log.Errorf("authorization err: %v", err)
		JSONError(c, http.StatusBadRequest, "authorization err")
		return
	}

	type response struct {
		Data string `json:"data"`
	}

	respMsg := control.transToStr(v)
	log.Infof("authorization resp body: %v", respMsg)
	var resp response
	_ = json.Unmarshal([]byte(respMsg), &resp)
	if resp.Data != "pass" {
		log.Errorf("authorization failed, data: %s", resp.Data)
		JSONError(c, http.StatusBadRequest, resp.Data)
		return
	}

	// 鉴权合法，先查询 app_id 是否已存在
	getOpts := db.AppGetOptions{
		IsFlag:   -1,
		UserName: req["username"],
	}
	result, err := db.App.Get(c, getOpts)
	if err != nil {
		log.Errorf("app get err: %v ", err)
		JSONError(c, http.StatusBadRequest, "app get err")
		return
	}

	opts := db.AppUpdateOptions{
		UserName:  req["username"],
		AppSecret: control.genUUID(),
	}

	if len(result.AppsInfo) == 0 {
		opts.AppName = req["appname"]
		opts.AppID = control.genAppID()
		opts.IsFlag = 0
		err = db.App.Create(c, opts)
	} else {
		opts.AppID = result.AppsInfo[0].AppID
		opts.IsFlag = -1 // 不更新 is_flag
		err = db.App.Update(c, opts)
	}
	if err != nil {
		log.Errorf("app upsert err: %v ", err)
		JSONError(c, http.StatusBadRequest, "app upsert err")
		return
	}

	// 再次查询
	result, err = db.App.Get(c, getOpts)
	if err != nil {
		log.Errorf("app get err: %v ", err)
		JSONError(c, http.StatusBadRequest, "app get err")
		return
	}

	var resp1 = struct {
		AppID     string `json:"appID"`
		AppSecret string `json:"appSecret"`
	}{
		AppID:     result.AppsInfo[0].AppID,
		AppSecret: result.AppsInfo[0].AppSecret,
	}
	JSONResp(c, http.StatusOK, resp1)
}

// GET /api/token/get
// AccessTokenGet 获取 AccessToken
func (control Controller) AccessTokenGet(c iris.Context) {
	log := log.GetLogger().GetCtx(c.Request().Context()).Sugar()
	appID := c.URLParam("appID")
	appSecret := c.URLParam("appSecret")

	if appID == "" {
		log.Error("appID is empty")
		JSONError(c, http.StatusBadRequest, "appID is empty")
		return
	}

	if appSecret == "" {
		log.Error("appSecret is empty")
		JSONError(c, http.StatusBadRequest, "appSecret is empty")
		return
	}

	getOpts := db.AppGetOptions{
		AppID:     appID,
		AppSecret: appSecret,
		IsFlag:    -1,
	}

	result, err := db.App.Get(c, getOpts)
	if err != nil {
		log.Errorf("app get err: %v ", err)
		JSONError(c, http.StatusBadRequest, "app get err")
		return
	}

	if len(result.AppsInfo) == 0 {
		log.Error("appID or appSecret is wrong")
		JSONError(c, http.StatusBadRequest, "appID or appSecret is wrong")
		return
	}

	if result.AppsInfo[0].IsFlag == 1 {
		log.Error("have no permission to get accessToken")
		JSONError(c, http.StatusBadRequest, "have no permission to get accessToken")
		return
	}

	// 生成 accessToken
	opts := db.AppUpdateOptions{
		AppID:       appID,
		AccessToken: control.genUUID(),
		IsFlag:      -1, // 不更新 is_flag
	}

	if err := db.App.Update(c, opts); err != nil {
		log.Errorf("app update err: %v ", err)
		JSONError(c, http.StatusBadRequest, "app update err")
		return
	}

	// 更新 redis
	if err := redis.SetDigestInfo(c, opts.AccessToken, appID, 2*60*60); err != nil {
		log.Errorf("set accessToken to redis err: %v", err)
		JSONError(c, http.StatusBadRequest, "set accessToken to redis error")
		return
	}

	// 从 redis 删除旧的 AccessToken
	oldToken := result.AppsInfo[0].AccessToken
	_, err = redis.GetDigestInfo(c, oldToken)
	if err == nil {
		_ = redis.SetDigestInfo(c, oldToken, appID, 1)
	}

	var resp = struct {
		AppID       string `json:"appID"`
		AccessToken string `json:"accessToken"`
	}{
		AppID:       appID,
		AccessToken: opts.AccessToken,
	}
	JSONResp(c, http.StatusOK, resp)
}

type taskReqMsg struct {
	TaskDir       string `json:"taskDir"`
	TaskName      string `json:"taskName"`
	TestData      string `json:"testData"`
	MailAddr      string `json:"mailAddr"`
	SysType       string `json:"sysType"`
	Timeout       int    `json:"timeout"`
	TraceID       string `json:"traceID"`
	BlockPassMail bool   `json:"blockPassMail"`
}

// CreateTask def
func (control Controller) CreateTask(c iris.Context) {
	log := log.GetLogger().GetCtx(c.Request().Context()).Sugar()

	var req taskReqMsg
	if err := util.TransJsonReq2Struct(c, &req); err != nil {
		log.Errorf("trans json to struct fail, err: %v", err)
		JSONError(c, http.StatusBadRequest, "trans json to struct error")
		return
	}

	if req.TraceID == "" {
		req.TraceID = c.GetHeader(common.TraceID)
	}

	taskReq := TaskReqMsg{
		MsgCmd:        cavaTaskReqMsg,
		TaskID:        control.genTraceID(),
		TaskDir:       req.TaskDir,
		TaskName:      req.TaskName,
		TestData:      req.TestData,
		NetID:         "cava_client",
		MailAddr:      req.MailAddr,
		SysType:       req.SysType,
		Timeout:       req.Timeout,
		TraceID:       req.TraceID,
		BlockPassMail: req.BlockPassMail,
	}

	// 写入任务通道
	taskReqChan <- taskReq

	var resp = struct {
		TaskID  string `json:"task_id"`
		TraceID string `json:"trace_id"`
	}{
		TaskID:  taskReq.TaskID,
		TraceID: taskReq.TraceID,
	}

	JSONResp(c, http.StatusOK, resp)
}

func (control Controller) proxyTransfer(c iris.Context, serverAddr string) {
	log := qflog.GetLogger().GetCtx(c.Request().Context()).Sugar()
	r := c.Request()

	url := fmt.Sprintf("%s%s", serverAddr, r.RequestURI)
	tmpData, err := ioutil.ReadAll(r.Body)
	if err != nil {
		log.Errorf("read all from request body err: %v", err)
		return
	}
	reqData := string(tmpData)
	contentType := c.GetHeader(common.ContentType)
	if !strings.Contains(contentType, "multipart/form-data") {
		log.Infof("req uri: %s method: %s, req data: %s, transfer to url: %s", r.RequestURI, r.Method, reqData, url)
	} else {
		log.Infof("req uri: %s method: %s, transfer to url: %s", r.RequestURI, r.Method, url)
	}

	reader := bytes.NewReader(tmpData)
	req, _ := http.NewRequest(r.Method, url, reader)
	for k, v := range r.Header {
		for _, vv := range v {
			req.Header.Add(k, vv)
		}
	}

	cli := &http.Client{}
	resp, err := cli.Do(req)

	if err != nil {
		log.Errorf("http client do err: %v", err)
		return
	}
	defer resp.Body.Close()

	for k, v := range resp.Header {
		for _, vv := range v {
			if strings.EqualFold(k, "Access-Control-Allow-Origin") || strings.EqualFold(k, common.TraceID) {
				continue
			}
			c.Header(k, vv)
		}
	}

	for _, value := range resp.Request.Cookies() {
		c.Header(value.Name, value.Value)
	}
	c.StatusCode(resp.StatusCode)

	result, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Errorf("read all from response body err: %v", err)
		return
	}

	_, err = c.Write(result)
	if err != nil {
		log.Errorf("write resp err: %v", err)
		return
	}
	log.Infof("response data: %s", string(result))
}

// ProxyHandler def
func (control Controller) ProxyHandler(c iris.Context) {
	addr := fmt.Sprintf("http://%s:%d", qfcfg.ServerConfig.Server.HttpIP, qfcfg.ServerConfig.Server.Httpport)
	control.proxyTransfer(c, addr)
}

// SmuProxyHandler def
func (control Controller) SmuProxyHandler(c iris.Context) {
	addr := fmt.Sprintf("http://%s:%d", qfcfg.ServerConfig.Server.IP, 10010)
	control.proxyTransfer(c, addr)
}
